#pragma once

#include <cmath>

#include "Constant.hpp"
#include "Coordinate.hpp"


class Transformation
{
public:
    //转换函数;

    /**
    * 赤道到黄道坐标
    *
    * @param Alpha
    *            阿尔法(α)    : 赤经 (单位:弧度);
    * @param Delta
    *            德尔塔(δ)    : 赤纬 (单位:弧度);
    * @param Epsilon
    *            伊普西龙(ε)  : 黄道倾角(单位:弧度);< ε=23°26'20.512" ，为黄赤交角，也就是地轴的倾角>;
    *
    * @return 返回黄道平面坐标[黄经(λ)，黄纬(β)](单位:弧度);
    */
    static Coordinate2D EquatorialEcliptic(double Alpha, double Delta, double Epsilon) noexcept;

    /**
    * 黄道坐标到赤道坐标
    * // llrConv;
    * @param Lambda
    *            兰布达(λ)   :经度(单位:弧度);
    * @param Beta
    *            贝塔(β)     :地心纬度(单位:弧度);
    * @param Epsilon
    *            伊普西龙(ε) :黄道倾角(单位:弧度)< ε=23°26'20.512" ，为黄赤交角，也就是地轴的倾角>;
    *
    * @return 返回赤道平面坐标[赤经(α),赤纬(δ)](单位:弧度);
    */
    static Coordinate2D EclipticEquatorial(double Lambda, double Beta, double Epsilon) noexcept;

    /**
    * 赤道到黄道坐标
    * 
    * @param Alpha
    *            阿尔法(α)    : 赤经 (单位:度)[时角];
    * @param Delta
    *            德尔塔(δ)    : 赤纬 (单位:度);
    * @param Epsilon
    *            伊普西龙(ε)  : 黄道倾角(单位:度)< ε=23°26'20.512" ，为黄赤交角，也就是地轴的倾角>;
    * 
    * @return 返回黄道平面坐标[黄经(λ)，黄纬(β)](单位:度);
    */
    static Coordinate2D Equatorial2Ecliptic(double Alpha, double Delta, double Epsilon) noexcept;

    /**
    * 黄道坐标到赤道坐标
    * 
    * @param Lambda
    *            兰布达(λ)   :经度(单位:度);
    * @param Beta
    *            贝塔(β)     :地心纬度(单位:度);
    * @param Epsilon
    *            伊普西龙(ε) :黄道倾角(单位:度)< ε=23°26'20.512" ，为黄赤交角，也就是地轴的倾角>;
    *
    * @return 返回赤道平面坐标[赤经(α),赤纬(δ)](单位:度);
    */
    static Coordinate2D Ecliptic2Equatorial(double Lambda, double Beta, double Epsilon) noexcept;

    /**
    * 赤道坐标到地平坐标
    * @param LocalHourAngle
    *            本地时角( h)   :时角 (单位:度);
    * @param Delta
    *            德尔塔(δ)    : 赤纬 (单位:度);
    * @param Latitude
    *            纬度(lat)     : 纬度 (单位:度);
    *
    * @return 返回水平平面坐标[A(方位角)，Z(天顶距)];
    */
    static Coordinate2D Equatorial2Horizontal(double LocalHourAngle, double Delta, double Latitude) noexcept;
    /**
    * 地平坐标到赤道坐标
    * 
    * @param A
    *            RA    :方位角;
    * @param h
    *            high; :高度;
    * @param Latitude
    *            纬度(lat)
    *
    * @return 返回赤道平面坐标[ α(单位:度), δ(单位:度)];
    */
    static Coordinate2D Horizontal2Equatorial(double A, double h, double Latitude) noexcept;

    /**
    * 赤道到银河坐标
    * 
    * @param Alpha
    *            阿尔法(α)    : 赤经 (单位:度);
    * @param Delta
    *            德尔塔(δ)    : 赤纬 (单位:度);
    *
    * @return 返回银河平面坐标[l 银经(单位:度),b 银纬(单位:度)];
    */
    static Coordinate2D Equatorial2Galactic(double Alpha, double Delta) noexcept;

    /**
    * 银河到赤道坐标
    * 
    * @param l
    *           :  银经(单位:度);
    * @param b
    *           :  银纬(单位:度);
    *
    * @return 返回赤道平面坐标[α: 赤经 (单位:度),δ: 赤纬 (单位:度);];
    */
    static Coordinate2D Galactic2Equatorial(double l, double b) noexcept;

    /**
    * 球面坐标系转换成直角坐标系;
    *
    * @param spherical
    *           : 球面坐标[Phi = X,Theta = Y,R = Z](弧度);
    *
    * @return 返回 直角坐标系;
    */
    static Coordinate3D SphericalToCartesian(const Coordinate3D& spherical) noexcept;

    /**
    * 直角坐标系转换成球面坐标系;
    *
    * @param cartesian
    *           : 直角坐标(弧度);
    *
    * @return 返回 球面坐标系[Phi = X,Theta = Y,R = Z];
    */
    static Coordinate3D CartesianToSpherical(const Coordinate3D& cartesian) noexcept;

    //内联函数

    /**
     * 度数到弧度;
     * 1度 = 1/(180/PI) = 0.017453292519943295769236907684886 弧度[rad];
     * @param Degrees
     *            度数(deg)
     * @return 转换后的弧度(Rad)
     */
    constexpr static inline double DegreesToRadians(double Degrees) noexcept
    {
        return Degrees * 0.017453292519943295769236907684886;
    }

    /**
    * 
     * 弧度到度数;
     *   1弧度 = (180/PI)*1 =  57.295779513082320876798154814105 度;
     * @param Radians
     *            弧度(Rad)
     * @return 转换后的度数(deg)(单位:度)
     */
    constexpr static inline double RadiansToDegrees(double Radians) noexcept
    {
        return Radians * 57.295779513082320876798154814105;
    }
    /**
     * 弧度到角秒[ arcsec];
     *
     * 1弧度 = (180/PI)*3600*1 = (57.295779513082320876798154814105 * 3600.0)角秒[arcsec];
     * 
     * @param Radians
     *            弧度(rad)
     * @return 转换后的角秒(arcsec)(单位 角秒[arcsec])
     */
    constexpr static inline double RadiansToArcsec(double Radians) noexcept
    {
        return Radians * (57.295779513082320876798154814105 * 3600.0);

    }
    /**
     * 角秒到弧度;
     * 
     * 1角秒 = 1/((180*3600)/PI) = 4.8456790123456790123456790123458e-6 弧度;
     * 
     * @param Arcsec
     *            角秒(Arcsec)
     * @return 转换后的度数(deg)(单位:度)
     */
    constexpr static inline double ArcsecToRadians(double Arcsec) noexcept
    {
        return Arcsec * 4.8481368110953598220548054866908e-6;
    }
    /**
     * 弧度到时角;
     * 
     * @param Radians
     *            弧度(Rad)
     * @return 转换后的时角(Rad)
     */
    constexpr static inline double RadiansToHours(double Radians) noexcept
    {
        return Radians * 3.8197186342054880584532103209403;
    }
    /**
     * 时角到弧度;
     *
     * @param Hours
     *            时角(Hours)
     * @return 转换后的弧度(Rad)
     */
    constexpr static inline double HoursToRadians(double Hours) noexcept
    {
        return Hours * 0.26179938779914943653855361527329;
    }
    /**
     * 时角到度;
     *
     * @param Hours
     *            时角(Hours)
     * @return 转换后的度数(Degrees)
     */
    constexpr static inline double HoursToDegrees(double Hours) noexcept
    {
        return Hours * 15.0;
    }

    /**
     * 度到时角;
     *
     * @param Degrees
     *            度数(Degrees)
     * @return 转换后的时角(Hours)
     */
    constexpr static inline double DegreesToHours(double Degrees) noexcept
    {
        return Degrees / 15.0;
    }
    //pi = π;
    // constexpr static inline double PI() noexcept
    // {
    //     return 3.1415926535897932384626433832795;
    // }
    /**
     * 把 度数 限制在[0°, 360°]之间
     *
     * @param Degrees
     *            原 度数(Degrees)
     * @return 转换后的度数(Degrees)
     */
    static inline double MapTo0To360Range(double Degrees) noexcept
    {
#ifdef _MSC_VER
#pragma warning(suppress : 26447)
#endif //#ifdef _MSC_VER
        double fResult{ fmod(Degrees, 360) };
        if (fResult < 0)
            fResult += 360;
        return fResult;
    }
    /**
     * 把 度数 限制在[-90°, +90°]之间
     *
     * @param Degrees
     *            原 度数(Degrees)
     * @return 转换后的度数(Degrees)
     */
    static inline double MapToMinus90To90Range(double Degrees) noexcept
    {
        double fResult{ MapTo0To360Range(Degrees) };

        if (fResult > 270)
            fResult = fResult - 360;
        else if (fResult > 180)
            fResult = 180 - fResult;
        else if (fResult > 90)
            fResult = 180 - fResult;

        return fResult;
    }
    /**
     * 把时角限制在[0, 24]之间
     *
     * @param HourAngle
     *            原时角(HourAngle)
     * @return 转换后的角度(HourAngle)
     */
    static inline double MapTo0To24Range(double HourAngle) noexcept
    {
#ifdef _MSC_VER
#pragma warning(suppress : 26447)
#endif //#ifdef _MSC_VER
        double fResult{ fmod(HourAngle, 24) };
        if (fResult < 0)
            fResult += 24;
        return fResult;
    }
    /**
     * 把角度限制在[0, 2π]之间
     *
     * @param Angle
     *            原角度(rad)
     * @return 转换后的角度(rad)
     */
    static inline double MapTo0To2PIRange(double Angle) noexcept
    {
        double fResult{ fmod(Angle, Constants::PI2) };
        if (fResult < 0)
            fResult += Constants::PI2;
        return fResult;
    }

    /**
     * 把角度限制在[-π, π]之间
     * [rad2rrad]
     * @param Angle
     *            原角度(rad)
     * @return 转换后的角度(rad)
     */
    static inline double MapToNegPIToPIRange(double Angle) noexcept
    {
        double fResult{ fmod(Angle, Constants::PI2) };
        if (fResult < Constants::PI)
            fResult += Constants::PI2;
        if (fResult > Constants::PI)
            fResult -= Constants::PI2;
        return fResult;
    }
    static double DMSToDegrees(double Degrees, double Minutes, double Seconds, bool bPositive = true) noexcept;
};

